/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company.  All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
*/

#include "FFIBinding.h"
#if 0 //PLATFORM_MAC || PLATFORM_IOS
#include "JSClassRegister.h"
#include "V8Utils.h"
#include "ffi.h"


static FuncPtr *GFuncArray = nullptr;
static uint32_t GFuncArrayLength = 0;

void SetFunctionArray(FuncPtr *FuncArray, uint32_t FuncArrayLength)
{
    GFuncArray = FuncArray;
    GFuncArrayLength = FuncArrayLength;
}

static v8::Local<v8::Uint8Array> WrapPointer(v8::Isolate* Isolate, void *Ptr, size_t Length)
{
    v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(Isolate, Ptr, Length);
    return v8::Uint8Array::New(ab, 0, Length);
}

static v8::Local<v8::Uint8Array> WrapPointer(v8::Isolate* Isolate, void *Ptr)
{
    return WrapPointer(Isolate, Ptr, 0);
}

static char* ArrayBufferData(v8::Local<v8::Value> Val)
{
    if (!Val->IsArrayBufferView()) return nullptr;
    auto ui = Val.As<v8::ArrayBufferView>();
    auto ab_c = ui->Buffer()->GetContents();
    return static_cast<char*>(ab_c.Data()) + ui->ByteOffset();
}

static size_t ArrayBufferLength(v8::Local<v8::Value> Val)
{
    if (!Val->IsArrayBufferView()) return 0;
    auto ui = Val.As<v8::ArrayBufferView>();
    return ui->ByteLength();
}

static bool IsArrayBuffer(v8::Local<v8::Value> Val)
{
    return Val->IsArrayBufferView();
}

inline int64_t GetInt64(v8::Local<v8::Value> Val) {
  return Val->IsNumber() ? (Val->IsBigInt() ? Val.As<v8::BigInt>()->Int64Value() : (int64_t)Val.As<v8::Number>()->Value()) : 0;
}

static void FFIPrepCif(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    unsigned int Nargs;
    char *Cif, *ArgTypes, *RetType;
    ffi_status Status;
    ffi_abi Abi;
    
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);

    
    if (Info.Length() != 5 ||
        !IsArrayBuffer(Info[0]) ||
        !IsArrayBuffer(Info[3]) ||
        !IsArrayBuffer(Info[4])
        )
    {
        puerts::FV8Utils::ThrowException(Isolate, "Bad parameters.");
        return;
    }
    
    Cif = ArrayBufferData(Info[0]);
    Abi = (ffi_abi)Info[1]->Uint32Value(Context).ToChecked();
    Nargs = Info[2]->Uint32Value(Context).ToChecked();
    RetType = ArrayBufferData(Info[3]);
    ArgTypes = ArrayBufferData(Info[4]);
    
    
    Status = ffi_prep_cif(reinterpret_cast<ffi_cif *>(Cif),
                          Abi,
                          Nargs,
                          reinterpret_cast<ffi_type *>(RetType),
                          reinterpret_cast<ffi_type **>(ArgTypes));

    Info.GetReturnValue().Set(v8::Integer::New(Isolate, Status));
}

static void FFIPrepCifVar(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    unsigned int FixArgs, TotoalArgs;
    char *Cif, *ArgTypes, *RetType;
    ffi_status Status;
    ffi_abi Abi;
    
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);

    
    if (Info.Length() != 6 ||
        !IsArrayBuffer(Info[0]) ||
        !IsArrayBuffer(Info[4]) ||
        !IsArrayBuffer(Info[5])
        )
    {
        puerts::FV8Utils::ThrowException(Isolate, "Bad parameters.");
        return;
    }
    
    Cif = ArrayBufferData(Info[0]);
    Abi = (ffi_abi)Info[1]->Uint32Value(Context).ToChecked();
    FixArgs = Info[2]->Uint32Value(Context).ToChecked();
    TotoalArgs = Info[3]->Uint32Value(Context).ToChecked();
    RetType = ArrayBufferData(Info[4]);
    ArgTypes = ArrayBufferData(Info[5]);
    
    
    Status = ffi_prep_cif_var(reinterpret_cast<ffi_cif *>(Cif),
                          Abi,
                          FixArgs, TotoalArgs,
                          reinterpret_cast<ffi_type *>(RetType),
                          reinterpret_cast<ffi_type **>(ArgTypes));

    Info.GetReturnValue().Set(v8::Integer::New(Isolate, Status));
}

class ClosureInfo
{
public:
    ClosureInfo(v8::Isolate* InIsolate, v8::Local<v8::Context> InContext,
                v8::Local<v8::Function> InFunction, void* InCodeLoc, ffi_closure *InClosure, size_t InRetSize)
        :CodeLoc(InCodeLoc), Isolate(InIsolate), Context(InIsolate, InContext),
            Function(InIsolate, InFunction), Closure(InClosure), RetSize(InRetSize)
    {
    }
    
    static void WeakCallback(const v8::WeakCallbackInfo<ClosureInfo>& Info)
    {
        ClosureInfo* Self = Info.GetParameter();
        Self->WeakCallback();
        delete Self;
    }
    
    void WeakCallback()
    {
        ffi_closure_free(Closure);
        int64_t ChangeInBytes = -static_cast<int64_t>(sizeof(*this));
        Isolate->AdjustAmountOfExternalAllocatedMemory(ChangeInBytes);
    }
    
    void Init(v8::Isolate* InIsolate, v8::Local<v8::ArrayBuffer> InBuffer)
    {
        Buffer.Reset(InIsolate, InBuffer);
        Buffer.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
        InIsolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this));
    }
    
    void *CodeLoc;
    
    v8::Isolate* Isolate;
    
    v8::Global<v8::Context> Context;
    
    v8::Global<v8::Function> Function;
    
    ffi_closure *Closure;
    
    size_t RetSize;
    
    v8::Global<v8::ArrayBuffer> Buffer;
};

static void ClosureWrap(ffi_cif *Cif, void *Ret, void** Args, void *UserData)
{
    ClosureInfo* CI = reinterpret_cast<ClosureInfo*>(UserData);
    
    v8::Isolate* Isolate = CI->Isolate;
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = CI->Context.Get(Isolate);
    v8::Context::Scope ContextScope(Context);
    
    v8::Local<v8::Value> Argv[2] = {WrapPointer(Isolate, Ret, CI->RetSize), WrapPointer(Isolate, Args, 0)};
    CI->Function.Get(CI->Isolate)->Call(Context, Context->Global(), 2, Argv);
}

static void FFIAllocClosure(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_alloc_closure: Uint32Array expected as #1 argument");
        return;
    }
    
    if (!Info[1]->IsFunction())
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_alloc_closure: function expected as #2 argument");
        return;
    }
    
    if (!Info[2]->IsNumber())
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_alloc_closure: number expected as #3 argument");
        return;
    }
    
    ffi_cif *Cif = reinterpret_cast<ffi_cif *>(ArrayBufferData(Info[0]));
    v8::Local<v8::Function> Callback = v8::Local<v8::Function>::Cast(Info[1]);
    size_t RetSize = Info[2]->Uint32Value(Context).ToChecked();
    
    void *CodeLoc;
    
    ffi_closure *Closure = reinterpret_cast<ffi_closure *>(ffi_closure_alloc(sizeof(ffi_closure), &CodeLoc));
    if (!Closure)
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_alloc_closure: alloc closure fail!");
        return;
    }
    
    ClosureInfo* CI = new ClosureInfo(Isolate, Context, Callback, CodeLoc, Closure, RetSize);
    
    ffi_status status = ffi_prep_closure_loc(
      Closure,
      Cif,
      ClosureWrap,
      CI,
      CodeLoc
    );
    
    if (status != FFI_OK)
    {
        ffi_closure_free(Closure);
        puerts::FV8Utils::ThrowException(Isolate, "ffi_alloc_closure: ffi_prep_closure_loc fail!");
        return;
    }
    
    v8::Local<v8::ArrayBuffer> AB = v8::ArrayBuffer::New(Isolate, CI, sizeof(ClosureInfo));
    
    CI->Init(Isolate, AB);
    
    Info.GetReturnValue().Set(v8::Uint8Array::New(AB, 0, sizeof(ClosureInfo)));
}

static void FFIFreeClosure(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_free_closure: invalid type for #1 argument");
        return;
    }
    
    if (ArrayBufferLength(Info[0]) != sizeof(ClosureInfo))
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_free_closure: #1 argument length not match");
        return;
    }
    
    ClosureInfo* CI = reinterpret_cast<ClosureInfo*>(ArrayBufferData(Info[0]));
    delete CI;
}

static void FFICall(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (Info.Length() != 4)
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_call(): requires 4 arguments!");
        return;
    }
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "ffi_call(): Uint32Array expected as #1 argument");
        return;
    }
    
    if (Info[1]->IsNumber())
    {
        if (Info[1]->Uint32Value(Context).ToChecked() >= GFuncArrayLength)
        {
             puerts::FV8Utils::ThrowException(Isolate, "ffi_call(): function index out of range!");
             return;
        }
    }

    char *Cif = ArrayBufferData(Info[0]);
    char *FuncPtr = Info[1]->IsNumber() ? reinterpret_cast<char *>(GFuncArray[Info[1]->Uint32Value(Context).ToChecked()]) : ArrayBufferData(Info[1]);
    char *ReturnPtr = ArrayBufferData(Info[2]);
    char *ArgsPtr = ArrayBufferData(Info[3]);

#if __OBJC__ || __OBJC2__
//    @try {
#endif
        ffi_call(
          reinterpret_cast<ffi_cif *>(Cif),
          FFI_FN(FuncPtr),
          reinterpret_cast<void *>(ReturnPtr),
          reinterpret_cast<void **>(ArgsPtr)
        );
#if __OBJC__ || __OBJC2__
//    } @catch (id ex) {
//        puerts::FV8Utils::ThrowException(Isolate, "ffi_call() throw exeption!");
//        return;
//    }
#endif

    Info.GetReturnValue().SetUndefined();
}

static void WritePointer(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "writePointer: Uint32Array expected as #1 argument");
        return;
    }
    auto Value = Info[1];
    if (!(Value->IsNull()|| IsArrayBuffer(Value)))
    {
        puerts::FV8Utils::ThrowException(Isolate, "writePointer: Uint32Array expected as #3 argument");
        return;
    }
    
    int64_t Offset = GetInt64(Info[2]);
    char *Ptr = ArrayBufferData(Info[0]) + Offset;
    
    if (Value->IsNull())
    {
        *reinterpret_cast<char **>(Ptr) = NULL;
    }
    else
    {
        char *ValuePtr = ArrayBufferData(Value);
        if (Info.Length() >= 4 && Info[3]->IsNumber())
        {
            ValuePtr += GetInt64(Info[3]);
        }
        *reinterpret_cast<char **>(Ptr) = ValuePtr;
    }
    
    Info.GetReturnValue().SetUndefined();
}

static void ReadPointer(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "readPointer: Uint32Array expected as #1 argument");
        return;
    }
    
    int64_t Offset = GetInt64(Info[1]);
    char *Ptr = ArrayBufferData(Info[0]) + Offset;
    
    char *Val = *reinterpret_cast<char **>(Ptr);
    
    size_t Size = !Info[2]->IsNull() ? Info[2]->Uint32Value(Context).ToChecked() : 0;
    
    Info.GetReturnValue().Set(WrapPointer(Isolate, Val, Size));
}

static void WriteUTF8String(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "WriteString: Uint32Array expected as #1 argument");
        return;
    }
    
    if (!Info[1]->IsString())
    {
        puerts::FV8Utils::ThrowException(Isolate, "WriteString: string expected as #2 argument");
        return;
    }
    
    v8::String::Utf8Value utf8Str(Isolate, Info[1]);
    
    
    int64_t Offset = GetInt64(Info[2]);
    if (utf8Str.length() >= ArrayBufferLength(Info[0]) - Offset)
    {
        puerts::FV8Utils::ThrowException(Isolate, "WriteString: no enough space");
        return;
    }
    
    char *Ptr = ArrayBufferData(Info[0]) + Offset;
    memcpy(Ptr, *utf8Str, utf8Str.length() + 1);
}

static void ReadUTF8String(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!IsArrayBuffer(Info[0]))
    {
        puerts::FV8Utils::ThrowException(Isolate, "ReadCString: Uint32Array expected as #1 argument");
        return;
    }
    
    int64_t Offset = GetInt64(Info[1]);
    char *Ptr = ArrayBufferData(Info[0]) + Offset;
    
    Info.GetReturnValue().Set(puerts::FV8Utils::ToV8String(Isolate, Ptr));
}

static void UTF8Length(const v8::FunctionCallbackInfo<v8::Value>& Info)
{
    v8::Isolate* Isolate = Info.GetIsolate();
    v8::Isolate::Scope IsolateScope(Isolate);
    v8::HandleScope HandleScope(Isolate);
    v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
    v8::Context::Scope ContextScope(Context);
    
    if (!Info[0]->IsString())
    {
        puerts::FV8Utils::ThrowException(Isolate, "WriteString: string expected as #2 argument");
        return;
    }
    
    v8::String::Utf8Value utf8Str(Isolate, Info[0]);
    
    Info.GetReturnValue().Set(utf8Str.length());
}

static void Init(v8::Isolate* Isolate, v8::Local<v8::Context> Context, v8::Local<v8::Object> Exports)
{
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "ffi_prep_cif"), v8::FunctionTemplate::New(Isolate, FFIPrepCif)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "ffi_prep_cif_var"), v8::FunctionTemplate::New(Isolate, FFIPrepCifVar)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "ffi_call"), v8::FunctionTemplate::New(Isolate, FFICall)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "writePointer"), v8::FunctionTemplate::New(Isolate, WritePointer)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "readPointer"), v8::FunctionTemplate::New(Isolate, ReadPointer)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "writeUTF8String"), v8::FunctionTemplate::New(Isolate, WriteUTF8String)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "readUTF8String"), v8::FunctionTemplate::New(Isolate, ReadUTF8String)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "UTF8Length"), v8::FunctionTemplate::New(Isolate, UTF8Length)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "ffi_alloc_closure"), v8::FunctionTemplate::New(Isolate, FFIAllocClosure)->GetFunction(Context).ToLocalChecked()).Check();
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "ffi_free_closure"), v8::FunctionTemplate::New(Isolate, FFIFreeClosure)->GetFunction(Context).ToLocalChecked()).Check();
    
#define SET_FFI_ENUM(value) \
    Exports->DefineOwnProperty(Context, puerts::FV8Utils::ToV8String(Isolate, #value), \
    v8::Integer::New(Isolate, (uint32_t)(value)), \
    static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))

    SET_FFI_ENUM(FFI_OK);
    SET_FFI_ENUM(FFI_BAD_TYPEDEF);
    SET_FFI_ENUM(FFI_BAD_ABI);
    
    SET_FFI_ENUM(FFI_DEFAULT_ABI);
    SET_FFI_ENUM(FFI_FIRST_ABI);
    SET_FFI_ENUM(FFI_LAST_ABI);
    SET_FFI_ENUM(FFI_TYPE_STRUCT);
#undef SET_FFI_ENUM
    
    auto Types = v8::Object::New(Isolate);
    
#define SET_FFI_TYPE(key, value) \
    Types->DefineOwnProperty(Context, puerts::FV8Utils::ToV8String(Isolate, key), \
    WrapPointer(Isolate, reinterpret_cast<char *>(&value)), \
    static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))
    
    SET_FFI_TYPE("void", ffi_type_void);
    SET_FFI_TYPE("uint8", ffi_type_uint8);
    SET_FFI_TYPE("int8", ffi_type_sint8);
    SET_FFI_TYPE("uint16", ffi_type_uint16);
    SET_FFI_TYPE("int16", ffi_type_sint16);
    SET_FFI_TYPE("uint32", ffi_type_uint32);
    SET_FFI_TYPE("int32", ffi_type_sint32);
    SET_FFI_TYPE("uint64", ffi_type_uint64);
    SET_FFI_TYPE("int64", ffi_type_sint64);
    SET_FFI_TYPE("float", ffi_type_float);
    SET_FFI_TYPE("double", ffi_type_double);
    SET_FFI_TYPE("pointer", ffi_type_pointer);
#undef SET_FFI_TYPE
    
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "FFI_TYPES"), Types).Check();
    
    auto SizeOf = v8::Object::New(Isolate);
#define SET_SIZEOF(key, type) \
    SizeOf->DefineOwnProperty(Context, puerts::FV8Utils::ToV8String(Isolate, key), \
    v8::Integer::New(Isolate, static_cast<uint32_t>(sizeof(type))), \
    static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))
    
    SET_SIZEOF("uint8", uint8_t);
    SET_SIZEOF("int8", int8_t);
    SET_SIZEOF("uint16", uint16_t);
    SET_SIZEOF("int16", int16_t);
    SET_SIZEOF("uint32", uint32_t);
    SET_SIZEOF("int32", int32_t);
    SET_SIZEOF("uint64", uint64_t);
    SET_SIZEOF("int64", int64_t);
    SET_SIZEOF("float", float);
    SET_SIZEOF("double", double);
    SET_SIZEOF("pointer", char *);
#undef SET_SIZEOF
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "sizeof"), SizeOf).Check();
    
    auto AlignOf = v8::Object::New(Isolate);
#define SET_ALIGNOF(name, type) \
    struct s_##name { type a; }; \
    AlignOf->DefineOwnProperty(Context, puerts::FV8Utils::ToV8String(Isolate, #name), \
        v8::Integer::New(Isolate, static_cast<uint32_t>(__alignof__(struct s_##name))), \
        static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))
    
    SET_ALIGNOF(uint8, uint8_t);
    SET_ALIGNOF(int8, int8_t);
    SET_ALIGNOF(uint16, uint16_t);
    SET_ALIGNOF(int16, int16_t);
    SET_ALIGNOF(uint32, uint32_t);
    SET_ALIGNOF(int32, int32_t);
    SET_ALIGNOF(uint64, uint64_t);
    SET_ALIGNOF(int64, int64_t);
    SET_ALIGNOF(float, float);
    SET_ALIGNOF(double, double);
    SET_ALIGNOF(pointer, char *);
#undef SET_ALIGNOF
    Exports->Set(Context, puerts::FV8Utils::ToV8String(Isolate, "alignof"), AlignOf).Check();
    
#define SET_Property(key, value) \
    Exports->DefineOwnProperty(Context, puerts::FV8Utils::ToV8String(Isolate, key), \
    v8::Integer::New(Isolate, (uint32_t)(value)), \
    static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))

    SET_Property("FFI_ARG_SIZE", sizeof(ffi_arg));
    SET_Property("FFI_SARG_SIZE", sizeof(ffi_sarg));
    SET_Property("FFI_TYPE_SIZE", sizeof(ffi_type));
    SET_Property("FFI_CIF_SIZE", sizeof(ffi_cif));
    SET_Property("POINTER_SIZE", sizeof(char *));
#undef SET_Property
}

PUERTS_MODULE(ffi_bindings, Init);

#endif
